package com.billdowney.intercept;

import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.billdowney.annotation.SystemLogInfo;
import com.billdowney.entity.system.SystemVisitRecord;
import com.billdowney.service.interfaces.system.SystemVisitRecordService;
import com.billdowney.service.interfaces.system.log.SystemLogService;
import com.billdowney.service.util.project.ProjectModelUtil;
import com.billdowney.util.CommonUtil;
import com.billdowney.util.regex.MatcherUtil;
import com.billdowney.util.request.RequestUtil;
import com.google.common.base.Joiner;

/**
 * 控制器访问记录拦截器
 * 
 * @author 超级小富翁 - BillDowney
 * @data 2017年9月11日 下午10:19:13
 */
@Component
public class VisitRecordInterceptor extends HandlerInterceptorAdapter {

	/**
	 * 相对于HandlerInterceptor，HandlerInterceptorAdapter多了一个实现方法afterConcurrentHandlingStarted()，
	 * 它来自HandlerInterceptorAdapter的直接实现类AsyncHandlerInterceptor,
	 * AsyncHandlerInterceptor接口直接继承了HandlerInterceptor，
	 * 并新添了afterConcurrentHandlingStarted()方法用于处理异步请求，
	 * 当Controller中有异步请求方法的时候会触发该方法时，异步请求先支持preHandle、然后执行afterConcurrentHandlingStarted。
	 * 异步线程完成之后执行preHandle、postHandle、afterCompletion
	 */

	@Autowired
	private SystemVisitRecordService visitRecordService;
	@Autowired
	private ProjectModelUtil projectModelUtil;
	@Autowired
	private SystemLogService systemLogService;

	/**
	 * 在DispatcherServlet完全处理完请求后被调用，可用于清理资源等。返回处理（已经渲染了页面）
	 */
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
	}

	/**
	 * 在业务处理器处理请求执行完成后，生成视图之前执行。后处理（调用了Service并返回ModelAndView，但未进行页面渲染），有机会修改ModelAndView。
	 */
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	}

	/**
	 * 在业务处理器处理请求之前被调用。预处理，可以进行编码、安全控制、权限校验等处理。
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handlerMethod)
			throws Exception {
		this.saveSystemLog(request, response, handlerMethod,
				this.saveSystemVisitRecord(request, response, handlerMethod));
		return true;
	}

	@Override
	public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	}

	/**
	 * 保存系统访问记录
	 * 
	 * @author: 超级小富翁 - BillDowney
	 * @date: 2019年4月29日 下午2:20:07
	 */
	public SystemVisitRecord saveSystemVisitRecord(HttpServletRequest request, HttpServletResponse response,
			Object handlerMethod) {
		try {
			// 检查是否需要保存request中的值
			if (projectModelUtil.isSaveVisitRecord()) {
				SystemVisitRecord visitRecord = new SystemVisitRecord();
				visitRecord.setServerName(request.getServerName());// 服务器名称
				visitRecord.setServerPort(request.getServerPort());// 服务器端口号
				visitRecord.setContextPath(request.getContextPath());// 项目目录地址
				visitRecord.setMethod(request.getMethod());// 客户机的请求方法
				visitRecord.setServletPath(request.getServletPath());// Servlet的映射路径
				visitRecord.setRequestURL(request.getRequestURL().toString());// 客户机请求的绝对url
				visitRecord.setRequestURI(request.getRequestURI());// 客户机请求的绝对uri
				visitRecord.setRemoteAddr(request.getRemoteAddr());// 发出请求的客户机的IP地址
				visitRecord.setRemoteHost(request.getRemoteHost());// 发出请求的客户机的完整主机名
				visitRecord.setRemotePort(request.getRemotePort());// 客户机所使用的网络端口号
				visitRecord.setRemoteUser(request.getRemoteUser());// 客户机认证的用户
				visitRecord.setProtocol(request.getProtocol());// 协议名称
				visitRecord.setLocalAddr(request.getLocalAddr());// WEB服务器的IP地址
				visitRecord.setLocalName(request.getLocalName());// WEB服务器的主机名
				visitRecord.setLocalPort(request.getLocalPort());// WEB服务器的端口号
				visitRecord.setCharacterEncoding(request.getCharacterEncoding());// 编码集
				visitRecord.setContentLength(request.getContentLength());// 内容长度
				visitRecord.setAuthType(request.getAuthType());// 认证类型
				visitRecord.setScheme(request.getScheme());// 当前链接使用的协议
				visitRecord.setLocale(request.getLocale().toString());// 浏览器当前语言
				visitRecord.setRequestedSessionId(request.getSession().getId());
				visitRecord.setRealIp(RequestUtil.getIpAddr(request));// 获取真实访问ip
				visitRecord.setClient(RequestUtil.getClientType(request));// 客户端类型
				visitRecord.setClientInfo(request.getHeader("user-agent"));// 客户端信息
//				if (isPringVisitRecord) {
//					System.out.println("==========开始：打印request中所有信息==========");
//					System.out.println("项目目录地址：" + request.getContextPath());
//					System.out.println("客户机的请求方法：" + request.getMethod());
//					System.out.println("打印头字段===========");
//					Enumeration<String> names = request.getHeaderNames();
//					while (names.hasMoreElements()) {
//						String key = (String) names.nextElement();
//						String value = request.getHeader(key);
//						System.out.println(key + ":" + value);
//					}
//					System.out.println("客户机的查询参数：" + request.getQueryString());
//					System.out.println("客户机请求的url：" + request.getRequestURL());
//					System.out.println("客户机请求的uri：" + request.getRequestURI());
//					System.out.println("Servlet的映射路径：" + request.getServletPath());
//					System.out.println("服务器的名称：" + request.getServerName());
//					System.out.println("端口号：" + request.getServerPort());
//					System.out.println("Request域中获取所有的字段名称=======");
//					Map<String, String[]> params = request.getParameterMap();
//					for (String key : params.keySet()) {
//						System.out.println(key + ":" + request.getParameter(key));
//					}
//					System.out.println("客户机的IP地址：" + request.getRemoteAddr());
//					System.out.println("主机名：" + request.getRemoteHost());
//					System.out.println("客户机所使用的网络端口号：" + request.getRemotePort());
//					System.out.println("协议名称：" + request.getProtocol());
//					System.out.println("请求相对的地址：" + request.getRequestURI());
//					System.out.println("WEB服务器的IP地址：" + request.getLocalAddr());
//					System.out.println("WEB服务器的主机名：" + request.getLocalName());
//					System.out.println("WEB服务器的端口号：" + request.getLocalPort());
//					System.out.println("字符集：" + request.getCharacterEncoding());
//					System.out.println("内容长度：" + request.getContentLength());
//					System.out.println("getPathInfo：" + request.getPathInfo());
//					System.out.println("getAuthType：" + request.getAuthType());
//					System.out.println("getPathTranslated：" + request.getPathTranslated());
//					System.out.println("客户机认证的用户：" + request.getRemoteUser());
//					System.out.println("当前链接使用的协议：" + request.getScheme());
//					System.out.println("浏览器当前语言：" + request.getLocale());
//					System.out.println("getRequestedSessionId：" + request.getRequestedSessionId());
//					System.out.println("request.getSession().getId()：" + request.getSession().getId());
//					System.out.println("获取真实访问ip：" + visitRecord.getRealIp());
//					System.out.println("客户端类型：" + visitRecord.getClient());
//					System.out.println("客户端信息：" + visitRecord.getClientInfo());
//					System.out.println("==========结束：打印request中所有信息==========");
//				}
				return visitRecordService.addVisitRecord(visitRecord);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 保存系统日志
	 * 
	 * @author: 超级小富翁 - BillDowney
	 * @date: 2019年4月29日 下午2:19:34
	 */
	public void saveSystemLog(HttpServletRequest request, HttpServletResponse response, Object handlerMethod,
			SystemVisitRecord visitRecord) {
		try {
			if (handlerMethod instanceof HandlerMethod) {
				HandlerMethod method = (HandlerMethod) handlerMethod;
				if (method.hasMethodAnnotation(SystemLogInfo.class)) {
					SystemLogInfo logInfo = method.getMethodAnnotation(SystemLogInfo.class);
					Map<String, Object> mapParams = CommonUtil.beanToMap(visitRecord);
					String logDesc = logInfo.value();
					Set<String> strs = MatcherUtil.getStrsBySet("\\{((?![\\{\\}]).)+\\}", logDesc);
					for (String str : strs) {
						String key = str.substring(1, str.length() - 1);
						StringBuffer value = new StringBuffer();
						if (key.contains("visit:")) {
							key = key.replace("visit:", "");
							value.append(mapParams.get(key));
						} else {
							value.append(Joiner.on(",").join(request.getParameterValues(key)));
						}
						logDesc = logDesc.replace(str, value);
					}
					systemLogService.save(logDesc, logInfo.handleType(), method.getBeanType(), logInfo.note());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
